﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using UnityEngine.Assertions;

namespace Nirvana
{
    [ExecuteInEditMode]
    [DisallowMultipleComponent]
    public class AudioManager : UnitySingleton<AudioManager>
    {
        private static Logger logger = LogSystem.GetLogger("AudioManager");
        private AudioSourcePool audioPool;
        private List<IPlayerAudio> playerAudioList = new List<IPlayerAudio>();

        private void Awake()
        {
            if(audioPool == null)
            {
                GameObject obj = new GameObject("Audio Source Pool");
                obj.transform.parent = transform;
                this.audioPool = new AudioSourcePool(transform, obj.transform);
            }

#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                EditorApplication.update +=  this.Update;
            }
            else
#endif
            {

                UnityEngine.Object.DontDestroyOnLoad(this.gameObject);
            }

        }
        private void Update()
        {
            playerAudioList.RemoveAll(OnRemove);
        }

        protected new void OnDestroy()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                EditorApplication.update -= this.Update;
            }
#endif
            this.Stop();
            base.OnDestroy();

        }

        private static bool OnRemove(IPlayerAudio audio)
        {
            if (audio.IsPlaying)
            {
                audio.Update();
                return false;
            }
            audio.Remove();
            return true;
        }


        public static IBasicAudio Play(AudioItem item)
        {
            return Play(item, Vector3.zero);
        }

        public static IBasicAudio Play(AudioItem item,Vector3 pos)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return PlayerAudioBasic.Instance;
            var audio = item.GetAudioItem(Instance.audioPool);
            audio.Set(pos);
            audio.Play();
            Instance.playerAudioList.Add(audio);
            return audio;
        }
        public static IBasicAudio Play(AudioItem item, Transform parent)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return PlayerAudioBasic.Instance;
            var audio = item.GetAudioItem(Instance.audioPool);
            audio.Set(parent);
            audio.Play();
            Instance.playerAudioList.Add(audio);
            return audio;
        }

        public static void PlayAndForget(AudioItem item)
        {
            PlayAndForget(item, Vector3.zero);
        }
        public static void PlayAndForget(AudioItem item, Vector3 position)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return;
            var audio = item.GetAudioItem(Instance.audioPool);
            audio.Set(position);
            audio.Play();
            Instance.playerAudioList.Add(audio);
        }
        public static void PlayAndForget(AudioItem item, Transform parent)
        {
            Assert.IsNotNull<AudioItem>(item);
            if (Instance == null || item == null) return;
            var audio = item.GetAudioItem(Instance.audioPool);
            audio.Set(parent);
            audio.Play();
            Instance.playerAudioList.Add(audio);
        }

        public static void PlayAndForget(string bundle, string asset)
        {
            PlayAndForget(new AssetID(bundle, asset));
        }

        public static void PlayAndForget(string bundle, string asset, Vector3 position)
        {
            PlayAndForget(new AssetID(bundle, asset), position);
        }
        public static void PlayAndForget(string bundle, string asset, Transform parent)
        {
            PlayAndForget(new AssetID(bundle, asset), parent);
        }
        public static void PlayAndForget(AssetID assetid)
        {
            if (Instance == null) return;
            Instance.StartCoroutine(WaitPlay(assetid));

        }

        public static void PlayAndForget(AssetID assetID, Vector3 position)
        {
            if (Instance == null)
            {
                return;
            }
            Instance.StartCoroutine(WaitPlay(assetID, position));
        }

        public static void PlayAndForget(AssetID assetID, Transform parent)
        {
            if (Instance == null)
            {
                return;
            }
            Instance.StartCoroutine(WaitPlay(assetID, parent));
        }

        public static void StopAll()
        {
            if (Instance == null)
            {
                return;
            }
            Instance.Stop();
        }
        private void Stop()
        {
            foreach (var p in this.playerAudioList)
            {
                p.Stop();
            }
        }
        private static IEnumerator WaitPlay(AssetID assetid, Vector3 pos)
        {
            WaitLoadScriptable waitLoadScriptable = ScriptablePool.Instance.Load(assetid);
            yield return waitLoadScriptable;
            if (!string.IsNullOrEmpty(waitLoadScriptable.Error))
            {
                logger.LogError("PlayAndForget {0} failed: {1}", assetid, waitLoadScriptable.Error);
                yield break;
            }
            AudioItem audioItem = waitLoadScriptable.LoadedObject as AudioItem;
            PlayAndForget(audioItem, pos);
            ScriptablePool.Instance.Free(audioItem, false);
            yield break;
        }
        private static IEnumerator WaitPlay(AssetID assetid, Transform parent)
        {
            WaitLoadScriptable waitLoadScriptable = ScriptablePool.Instance.Load(assetid);
            yield return waitLoadScriptable;
            if (!string.IsNullOrEmpty(waitLoadScriptable.Error))
            {
                logger.LogError("PlayAndForget {0} failed: {1}", assetid, waitLoadScriptable.Error);
                yield break;
            }
            AudioItem audioItem = waitLoadScriptable.LoadedObject as AudioItem;
            PlayAndForget(audioItem, parent);
            ScriptablePool.Instance.Free(audioItem, false);
            yield break;
        }
        private static IEnumerator WaitPlay(AssetID assetid)
        {
            WaitLoadScriptable waitLoadScriptable = ScriptablePool.Instance.Load(assetid);
            yield return waitLoadScriptable;
            if (!string.IsNullOrEmpty(waitLoadScriptable.Error))
            {
                logger.LogError("PlayAndForget {0} failed: {1}", assetid, waitLoadScriptable.Error);
                yield break;
            }
            AudioItem audioItem = waitLoadScriptable.LoadedObject as AudioItem;
            PlayAndForget(audioItem);
            ScriptablePool.Instance.Free(audioItem, false);
            yield break;
        }
    }
}

